package jp.co.mclnet.util;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import jp.co.mclnet.exception.AlreadyExistsException;
import jp.co.mclnet.exception.AlreadyUpdatedException;
import jp.co.mclnet.exception.LogicalException;
import jp.co.mclnet.exception.NotFoundException;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.apache.poi.ss.usermodel.Cell;

public class ExcelDaoShimanuki extends ExcelDao {

    private String filePath;
    private String tableName;

    @Override
    public <E extends Serializable> void create(E value)
            throws AlreadyExistsException, LogicalException {
        throw new UnsupportedOperationException();
    }

    @Override
    public <E extends Serializable> int delete(E condition)
            throws NotFoundException {
        throw new UnsupportedOperationException();
    }

    @Override
    public <E extends Serializable> void deleteByKey(E value)
            throws NotFoundException, AlreadyUpdatedException, LogicalException {
        throw new UnsupportedOperationException();
    }

    @Override
    public String getFilePath() {
        return filePath;
    }

    @Override
    public String getTableName() {
        return tableName;
    }

    @Override
    public <E extends Serializable> Iterator<E> like(E condition)
            throws NotFoundException, LogicalException {
        throw new UnsupportedOperationException();
    }

    @Override
    public <E extends Serializable> Iterator<E> read(E obj) {

        // objは結果の型生成のためだけに使用しています。
        // 条件としては使っていません

        // テーブルを探してリストに詰める
        InputStream is = null;
        try {
            try {
                is = new FileInputStream(getFilePath());
            } catch (FileNotFoundException e) {
                throw new IllegalArgumentException(e);
            }
            POIFSFileSystem pfs = new POIFSFileSystem(is);
            HSSFWorkbook book = new HSSFWorkbook(pfs);

            // テーブル名に一致するsheetがあるかチェックする。
            HSSFSheet sheet = null;
            for (int z = 0; z < book.getNumberOfSheets(); z++) {
                HSSFSheet tmp = book.getSheetAt(z);
                if (tmp.getSheetName().equals(getTableName())) {
                    sheet = tmp;
                }
            }
            if (sheet == null) {
                throw new IllegalArgumentException(getTableName());
            }

            int firstRowNum = sheet.getFirstRowNum();
            int lastRowNum = sheet.getLastRowNum();

            Integer cellStart = null;
            Integer cellEnd = null;

            // シートを一行ずつ
            List<List<Cell>> readTable = new ArrayList<List<Cell>>();
            for (int y = firstRowNum; y <= lastRowNum; y++) {
                HSSFRow row = (HSSFRow) sheet.getRow(y);
                if (row == null) {
                    continue;
                }
                int firstCellNum = row.getFirstCellNum();
                int lastCellNum = row.getLastCellNum();

                // 行の中の、セルを一つずつ
                ArrayList<Cell> cellList = new ArrayList<Cell>();
                for (int x = firstCellNum; x <= lastCellNum; x++) {
                    HSSFCell cell = (HSSFCell) row.getCell((int) x);
                    if (cell == null) {
                        // テーブル開始前判定(2周目以降)
                        if (cellStart != null && x < cellStart) {
                            continue;
                        }
                        // テーブル過ぎた判定(2周目以降)
                        if (cellEnd != null && x >= cellEnd) {
                            continue;
                        }
                        // 範囲外かつ終了(初回のみ)
                        if (cellStart != null && cellEnd == null) {
                            cellEnd = x;
                            continue;
                        }
                    }
                    if (cellStart == null) {
                        cellStart = x;
                    }
                    cellList.add(cell);
                }
                readTable.add(cellList);
            }

            // カラム名をリストに詰める
            List<Object> columnName = new ArrayList<Object>();
            for (Cell cell : readTable.get(0)) {
                Object value = CalcUtils.getValue(cell);
                // TODO キャメルケースにする
                columnName.add(value);
            }

            // カラム名以降の値を詰めていく
            List<E> result = new ArrayList<E>();
            for (int i = 1; i < readTable.size(); i++) {
                List<Cell> row = readTable.get(i);
                E clone = clone(obj);
                for (int j = 0; j < row.size(); j++) {
                    Cell cell = row.get(j);
                    String name = (String) columnName.get(j);
                    Object value = CalcUtils.getValue(cell);
                    Utils.setter(clone, name, value);
                }
                result.add(clone);
            }

            return result.iterator();

        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException e) {
                    throw new IllegalStateException(e);
                }
            }
        }

    }

    /**
     * {@link Serializable}のインスタンスを複製する。
     * @param instance 複製したいインスタンス
     * @return 処理結果
     */
    protected static final <E extends Serializable> E clone(E instance) {
        return (E) Utils.clone(instance);
    }

    @Override
    public <E extends Serializable> void readByKey(E value)
            throws NotFoundException, LogicalException {
        throw new UnsupportedOperationException();
    }

    @Override
    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }

    @Override
    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    @Override
    public <E extends Serializable> int update(E value, E condition)
            throws NotFoundException {
        throw new UnsupportedOperationException();
    }

    @Override
    public <E extends Serializable> void updateByKey(E value)
            throws NotFoundException, AlreadyUpdatedException, LogicalException {
        throw new UnsupportedOperationException();

    }
}
